/**
 * 为图片添加文字水印或者二维码或者同时添加两者
 * @author sunjing
 * @version 1.1
 */
;(function (global) {
    "use strict"
    global.Watermark = {}

    /**
     * 将blob对象转换为img对象
     * @param {Blob} blob 原始图片blob对象
     * @return {Promise<HTMLImageElement>} HTMLImageElement
     */
    function blobToImg(blob) {
        return new Promise(((resolve) => {
            const img = new Image()
            img.src = URL.createObjectURL(blob)
            img.addEventListener('load', () => resolve(img))
        }))
    }

    /**
     * blob类型图片转为canvas元素
     * @param {Blob} blob 原始图片blob对象
     * @return {Promise<HTMLCanvasElement>} canvas元素
     */
    async function blobToCanvas(blob) {
        // blob转为位图图像
        const imageBitmap = await createImageBitmap(blob)
        const canvas = document.createElement('canvas')
        // 设置canvas尺寸
        canvas.width = imageBitmap.width
        canvas.height = imageBitmap.height
        // 获取canvas上下文
        const ctx = canvas.getContext('2d')
        // 将图片绘入canvas
        ctx.drawImage(imageBitmap, 0, 0)
        return canvas
    }

    /**
     * 为图像添加文字水印
     * @param {HTMLCanvasElement} canvas canvas元素
     * @param option 配置项
     * @param {String} [option.font='24px 宋体'] 文字字体
     * @param {String} [option.textColor='#FFC82C'] 文字颜色
     * @param {String} [option.textAlign='right'] 文字对其方式
     * @param {String} option.textContent 文字内容
     * @param {Number[]|function} [option.textPos=[canvas.width - 20, canvas.height - 20]] 文字位置,[x,y]。如果是函数,入参为canvas元素和二维码尺寸
     */
    function fillTextWatermark(canvas, option) {
        let {
            font = '24px 宋体',
            textColor = '#FFC82C',
            textAlign = 'right',
            textContent,
            textPos = [canvas.width - 20, canvas.height - 20]
        } = option

        // 回调处理
        if (typeof textPos === 'function') {
            const newTextPos = textPos(canvas, qrcodeSize[0])
            if (Array.isArray(newTextPos) && newTextPos.length === 2) {
                textPos = newTextPos
            } else {
                console.error(`文字位置回调必须返回一个长度为二的数字数组,实际返回值:${newTextPos}`)
            }
        }

        return new Promise((resolve => {
            const canvasCtx = canvas.getContext('2d')

            /* 文字水印 */
            // 字体
            canvasCtx.font = font
            // 字体颜色
            canvasCtx.fillStyle = textColor
            // 对其方式
            canvasCtx.textAlign = textAlign
            // 绘制文字水印
            canvasCtx.fillText(textContent, textPos[0], textPos[1])

            canvas.toBlob(blob => resolve(blob))
        }))
    }

    /**
     * 为图像添加二维码水印
     * @param {HTMLCanvasElement} canvas canvas元素
     * @param option 配置项
     * @param {String} [option.qrcodeContent] 二维码内容
     * @param {Number[]} [option.qrcodeSize] 二维码尺寸,[宽,高],默认为图片长边*0.16,最大为[256,256]
     * @param {number} [option.qrcodeRatio=0.16] 二维码占原图片长边的比例,在未指定尺寸时生效,默认为0.16
     * @param {Number[]|function} [option.qrcodePos=[20, 图像高 - 二维码尺寸 - 20]] 二维码位置,[图片x轴左上角坐标,图片y轴左上角坐标]。如果是函数,入参为canvas元素和二维码尺寸
     * @return {Promise<Blob>} 处理后的图片blob对象
     */
    function drawQRCode(canvas, option) {
        let {
            qrcodeContent,
            qrcodeRatio = 0.16,
            qrcodeSize = getQRCodeDefaultSize(canvas, qrcodeRatio),
            qrcodePos = [20, canvas.height - qrcodeSize[0] - 20]
        } = option

        if (typeof qrcodePos === 'function') {
            const newQrcodePos = qrcodePos(canvas, qrcodeSize[0])
            if (Array.isArray(newQrcodePos) && newQrcodePos.length === 2) {
                qrcodePos = newQrcodePos
            } else {
                console.error(`二维码位置回调必须返回一个长度为二的数字数组,实际返回值:${newQrcodePos}`)
            }
        }

        return new Promise((resolve => {
            const canvasCtx = canvas.getContext('2d')

            /* 二维码水印 */
            const qrcodeDiv = document.createElement('div')
            // 创建二维码
            new QRCode(qrcodeDiv, {
                text: qrcodeContent,
                width: qrcodeSize[0],
                height: qrcodeSize[1],
                correctLevel: QRCode.CorrectLevel.Q
            })
            // 添加图片水印
            canvasCtx.drawImage(qrcodeDiv.querySelector('canvas'), qrcodePos[0], qrcodePos[1])
            canvas.toBlob(blob => resolve(blob))
        }))
    }

    /**
     * 为图像添加文字和二维码水印
     * @param {HTMLCanvasElement} canvas canvas元素
     * @param option 配置项
     * @param {String} [option.font='24px 宋体'] 文字字体
     * @param {String} [option.textColor='#FFC82C'] 文字颜色
     * @param {String} [option.textAlign='right'] 文字对其方式
     * @param {String} option.textContent 文字内容
     * @param {Number[]|function} [option.textPos=[canvas.width - 20, canvas.height - 20]] 文字位置,[x,y]。如果是函数,入参为canvas元素和二维码尺寸
     * @param {String} [option.qrcodeContent=option.textContent] 二维码内容
     * @param {Number[]} [option.qrcodeSize] 二维码尺寸,[宽,高],默认为图片长边*0.16,最大为[256,256]
     * @param {number} [option.qrcodeRatio=0.16] 二维码占原图片长边的比例,在未指定尺寸时生效,默认为0.16
     * @param {Number[]|function} [option.qrcodePos=[20, 图像高 - 二维码尺寸 - 20]] 二维码位置,[图片x轴左上角坐标,图片y轴左上角坐标]。如果是函数,入参为canvas元素和二维码尺寸
     * @return {Promise<Blob>} 处理后的图片blob对象
     */
    async function attachAll(canvas, option) {
        option.qrcodeContent = option.textContent
        await fillTextWatermark(canvas, option)
        const result = await drawQRCode(canvas, option)
        return new Promise((resolve) => resolve(result))
    }

    /**
     * 获取二维码默认尺寸,最大为[256,256],默认为图片长边*0.16
     * @param {HTMLCanvasElement} canvas canvas元素
     * @param {number} ratio 占原图片长边的比例
     * @return {number[]} 二维码宽高数组
     */
    function getQRCodeDefaultSize(canvas, ratio) {
        const longSide = Math.max(canvas.width, canvas.height)
        const size = longSide * ratio > 256 ? 256 : longSide * ratio
        return [size, size]
    }

    /**
     * 为图像附加文字和二维码水印
     * @param {File} imgFile 图片blob对象
     * @param options 配置项
     * @param {String} [options.font='24px 宋体'] 文字字体
     * @param {String} [options.textColor='#FFC82C'] 文字颜色
     * @param {String} [options.textAlign='right'] 文字对其方式
     * @param {String} options.textContent 文字内容
     * @param {Number[]|function} [options.textPos=[canvas.width - 20, canvas.height - 20]] 文字位置,[x,y]。如果是函数,入参为canvas元素和二维码尺寸
     * @param {String} [options.qrcodeContent=options.textContent] 二维码内容
     * @param {Number[]} [options.qrcodeSize] 二维码尺寸,[宽,高],默认为图片长边*0.16,最大为[256,256]
     * @param {number} [options.qrcodeRatio=0.16] 二维码占原图片长边的比例,在未指定尺寸时生效,默认为0.16
     * @param {Number[]|function} [options.qrcodePos=[20, 图像高 - 二维码尺寸 - 20]] 二维码位置,[图片x轴左上角坐标,图片y轴左上角坐标]。如果是函数,入参为canvas元素和二维码尺寸
     * @return {Blob} 加完水印的图片blob对象
     */
    Watermark.attachAll = async function (imgFile, options) {
        return await attachAll(await blobToCanvas(imgFile), options)
    }

    /**
     * 为图像附加文字水印
     * @param {File} imgFile 图片file对象
     * @param options 配置项
     * @param {String} [options.font='24px 宋体'] 文字字体
     * @param {String} [options.textColor='#FFC82C'] 文字颜色
     * @param {String} [options.textAlign='right'] 文字对其方式
     * @param {String} options.textContent 文字内容
     * @param {Number[]|function} [options.textPos=[canvas.width - 20, canvas.height - 20]] 文字位置,[x,y]。如果是函数,入参为canvas元素和二维码尺寸
     * @return {Blob} 加完水印的图片blob对象
     */
    Watermark.attachText = async function (imgFile, options) {
        return await fillTextWatermark(await blobToCanvas(imgFile), options)
    }

    /**
     * 为图像附加二维码
     * @param {File} imgFile 图片file对象
     * @param options 配置项
     * @param {String} [options.qrcodeContent=options.textContent] 二维码内容
     * @param {Number[]} [options.qrcodeSize] 二维码尺寸,[宽,高],默认为图片长边*0.16,最大为[256,256]
     * @param {number} [options.qrcodeRatio=0.16] 二维码占原图片长边的比例,在未指定尺寸时生效,默认为0.16
     * @param {Number[]|function} [options.qrcodePos=[20, 图像高 - 二维码尺寸 - 20]] 二维码位置,[图片x轴左上角坐标,图片y轴左上角坐标]。如果是函数,入参为canvas元素和二维码尺寸
     * @return {Blob} 加完水印的图片blob对象
     */
    Watermark.attachQRCode = async function (imgFile, options) {
        return await drawQRCode(await blobToCanvas(imgFile), options)
    }

    /**
     * 将blob对象转换为img对象
     * @param blob 图片blob对象
     * @type {function(Blob): Promise<HTMLImageElement>}
     * @return {Promise<HTMLImageElement>} HTMLImageElement
     */
    Watermark.blobToImg = blobToImg
}(this))
